﻿#!/usr/bin/env python
#_*_encoding:utf-8_*_ 
from head import *
import copy,sys

class YLBms:
    #head field
    player          = None
    genre           = None
    title           = None
    artist          = None
    playlevel       = None
    bpm             = None
    rank            = None
    wav             = None
    id              = None
    roundgap        = '16'

#全局变量

obj = YLBms()   #该bms对应的对象
gAllSec = []    #所有的节
gRoundSection = []  #所有对应的所有节
gLongKey = ['51','52','53','54','55']   #长按键
gLongInfo = {}  #长按键信息，key=roundindex,value是个串，标识了这轮是开始还是结束，或者是开始和结束
gAllround = []  #所有轮对象
gBreakPoint = {}    #所有空轮信息，以此在生成xml时插入空轮

def LoadYLBMS(name):
    global obj,gAllSec,gRoundSection
    Line = []
    f = open(name)
    for item in f:
        item = item.strip()
        if item:
            Line.append(item)
    f.close()
    datafield = []
    for i in xrange(len(Line)):
        p = Line[i].split(' ')

        if len(p) == 2:
            #head field
            if p[0] == '#PLAYER':
                obj.player = p[1]
                continue
        
            elif p[0] == '#GENRE':
                obj.genre = p[1]
                continue
        
            elif p[0] == '#TITLE':
                obj.title = p[1].decode('gbk').encode('utf-8')
                continue

            elif p[0] == '#ARTIST':
                obj.artist = p[1]
                continue

            elif p[0] == '#BPM':
                obj.bpm = p[1]
                continue

            elif p[0] == '#PLAYLEVEL':
                obj.playlevel = p[1]
                continue

            elif p[0] == '#RANK':
                obj.rank = p[1]
                continue

            elif p[0] == '#WAV02':
                obj.wav = p[1]
                continue
            
        if len(p) == 1 and len(Line[i].split(':')) ==2:
            #data field
            datafield.append(Line[i])

    seclist = ProcessYL(datafield)
    gAllSec = copy.copy(seclist)
    SetRoundSection()
    for i in xrange(len(gRoundSection)):
        Create(gRoundSection[i])

def ProcessYL(data):
    sectionlist = []
    length = len(data)
    #lastsection = 0
    curSection = []
    j = 0
    for i in xrange(length):
        sectionNum = int(data[i].split(':')[0][1:])/100

        if int(sectionNum) == 0:
            continue
        
        if sectionNum != int(data[i-1].split(':')[0][1:])/100 and j!=0:
            sectionlist.append(curSection)
            curSection = []
        else:
            #走到这，说明是该节的行
            pass
        curSection.append(data[i][1:])
        j += 1
        if i== length-1:
            #最后一行，因为没有下一节了，所以强行dump
            sectionlist.append(curSection)
    return sectionlist

class RoundYL():
    '''
    rule like 0x00000000
    每个rule有16位，可表示8个键，1节表示4rule，表示半轮，两节是一轮（当前的TG定义）
    若以后还要更多的键，再扩展这个rule长度
    '''
    def __init__(self):
        self.ft = '1'
        self.rule = {0:0,1:0,2:0,3:0,4:0,5:0,6:0,7:0}
        self.start = ''

def Create(data):
    thisround = RoundYL()
    num = YLKeyNum*2
    if len(data) ==1 and len(data[0]) == 1 and data[0][0].split(':')[0][-2:] == '16' and data[0][0].split(':')[1][0] == 'A':
        #如果是插空轮的标记，直接跳过
        gBreakPoint[len(gAllround)] = int(data[0][0].split(':')[1][1])
        return
    for item in data[0]:
        p = item.split(':')
        key = int(p[0])%100
        body = p[1]
        if key == 16 and body[0:2] == '07':
            thisround.ft = '2'
        if key == 16 and body[0:2] == 'A':
            gBreakPoint[len(gAllround)] = int(data[0].split(':')[1][1])
            #这里只是加空轮而已
            return
        key = StrToKey(key)
        AsignKeyByPos(key,body,thisround,0, num, enmGameMode_YL)
    
    if len(data) == 2:
        #说明是fevertime
        for item in data[1]:
            p = item.split(':')
            key = int(p[0])%100
            body = p[1]
            if key == 16 and body[0:2] == '07':
                thisround.ft = '2'
            if key == 16 and body[0:2] == 'A':
                gBreakPoint[len(gAllround)] = int(data[0].split(':')[1][1])
            key = StrToKey(key)
            AsignKeyByPos(key,body,thisround,1, num, enmGameMode_YL)
    gAllround.append(thisround)


def StrToKey(s):
    if s == 11 or s==51:
        return enmKeyValue_LEFT
    elif s == 12 or s==52:
        return enmKeyValue_UP
    elif s == 13 or s==53:
        return enmKeyValue_DOWN
    elif s == 14 or s==54:
        return enmKeyValue_RIGHT
    elif s == 15 or s==55:
        return enmKeyValue_SPACE
    else:
        return enmKeyValue_None


def SetRoundSection():
    global gAllSec, gRoundSection
    flag = 1
    curRound = []
    for item in gAllSec:
#        if len(item) == 1 and item[0].split(':')[0][-2:]=='16':
#            #可能是空轮
#            continue
        #如果不是空轮，再检查是否有按键07出现
        for i in xrange(len(item)):
            key = int(item[i].split(':')[0])%100
            body = item[i].split(':')[1]
            if flag==2:
                #说明是fevertime的第二节
                flag = 3
                break
            elif key==16 and body[0:2]=='07' and flag==1:
                #说明是fevertime,这轮有两节
                flag = 2
                break
            else:
                #说明是普通节，一轮,而且还没找到07
                flag = 1
        curRound.append(item)
        if flag == 1:        
            gRoundSection.append(curRound)
            curRound = []
        elif flag == 2:
            pass
        elif flag == 3:
            gRoundSection.append(curRound)
            curRound = []
            flag = 1 #标记置回来


def SaveYL(name):
    if name.split('.')[1].lower() != 'bms':
        return False
    
    LoadYLBMS(name)
    #AddSound()
    Modify()
    PickLongPress()
    ModifyByLongInfo()
        
    doc = Document()
    root = doc.createElement('musics')
    doc.appendChild(root)

    music = doc.createElement('music')
    music.setAttribute('id',name.split('.')[0])
    music.setAttribute('level',obj.playlevel)
    music.setAttribute('roundgap','8')
    music.setAttribute('gridcount','8')
    root.appendChild(music)
    
    i = 0
    for item in gAllround:
        round = doc.createElement('round')
        round.setAttribute('type',item.ft)
        
        if item.ft == '2':
            for k in xrange(8):
                rule = item.rule[k]
                if not rule:
                    rule = '0x00'
                round.setAttribute('rule%d'%(k+1),rule)
        elif item.ft == '1':
            for k in xrange(4):
                rule = item.rule[k]
                if not rule:
                    rule = '0x00'
                round.setAttribute('rule%d'%(k+1),rule)
        music.appendChild(round)
        #轮号加1
        i += 1
        
        num = gBreakPoint.get(i)
        if num!=None and num!=0:
            #说明在这一节有空轮，先加空轮
            for j in xrange(num):
                emptyround = doc.createElement('round')
                emptyround.setAttribute('type','3')
                music.appendChild(emptyround)
                #插入空轮，轮号也要加
                i += 1

    s = doc.toprettyxml(indent='    ',encoding='UTF-8')
    if name[0] == '3':
        folder = 'TG'
    elif name[0] == '4':
        folder = 'YL'

    if not MACRO_DEBUG:
        f = open('..'+os.sep+folder+os.sep+name.split('.')[0]+'.xml','w')
    else:
        f = open(name.split('.')[0]+'.xml','w')
    f.write(s)
    f.close()
    return True

def Modify():
    global gAllround
    for item in gAllround:
        for k in item.rule.keys():
            item.rule[k] = int2hex(item.rule[k])

def PickLongPress():
    global gRoundSection, gLongInfo
    LongPress = False
    roundindex = 0
    CurLongKey = None
    Start = None
    End = None
    for item in gRoundSection:
        #扫描前半轮中的节是否有长按
        ruleLen = YLKeyNum*2
        l = len(item)
        data = item[0]
        if roundindex==14:
            #说明是空轮，直接跳过
            pass
        if l==1 and len(data) == 1 and data[0].split(':')[0][-2:]=='16' and data[0].split(':')[1][0] == 'A':
            continue
        for line in item[0]:
            body = line.split(':')[1]
            key = line.split(':')[0][-2:]
            if key in gLongKey and int(body)!=0:
                CurLongKey = key
                body = Align(body,32)
#                i = 0
                #找长按开始与结束键位
                for s in xrange(len(body)/ruleLen):
                    pos = body[s*ruleLen:(s+1)*ruleLen]
                    if int(pos) == 0:
                        continue
                    if int(pos) !=0:
                        for j in xrange(len(pos)/2):
                            if pos[2*j:(j+1)*2] != '00' and Start==None:
                                #说明是开始
                                #键位：一轮共8 rule,32键位,一个键位1字节
                                Start = 8*s+j
                                LongPress = True
                                gLongInfo[roundindex] = 'k:%s;s:%d'%(CurLongKey,Start)
                            elif pos[2*j:(j+1)*2] != '00' and Start != None and LongPress == True:
                                #说明是结束,轮次：开始：结束
                                End = 8*s+j
                                has = gLongInfo.get(roundindex)
                                if not has:
                                    #说明本轮没有开始
                                    gLongInfo[roundindex] = 'k:%s;e:%d'%(CurLongKey,End)
                                else:
                                    #说明本轮开始的，字典里已经存在
                                    gLongInfo[roundindex] += ';e:%d'%(End)
                                #清标记
                                Start=None
                                End  =None
                                CurLongKey = None
                                LongPress = False
        #如果有，扫描后半轮
        if l==2:
            #本轮是fevertime
            for line in item[1]:
                body = line.split(':')[1]
                key = line.split(':')[0][-2:]
                if key in gLongKey and int(body)!=0:
                    #开始
                    CurLongKey = key
                    body = Align(body,32)
                    #找长按开始与结束键位
                    for s in xrange(len(body)/ruleLen):
                        pos = body[s*ruleLen:(s+1)*ruleLen]
                        if int(pos) == 0:
                            continue
                        if int(pos) !=0:
                            for j in xrange(len(pos)/2):
                                if pos[2*j:(j+1)*2] != '00' and Start==None:
                                    #说明是开始
                                    #键位：一轮共8 rule,32键位,一个键位1字节
                                    Start = 8+8*s+j
                                    gLongInfo[roundindex] = 'k:%s;s:%d'%(CurLongKey,Start)
                                elif pos[2*j:(j+1)*2] != '00' and Start != None and LongPress==True:
                                    #说明是结束,轮次：开始：结束
                                    End = 16+8*s+j
                                    has = gLongInfo.get(roundindex)
                                    if not has:
                                        #说明本轮没有开始
                                        gLongInfo[roundindex] = 'k:%s;e:%d'%(CurLongKey,End)
                                    else:
                                        #说明本轮开始的，字典里已经存在
                                        gLongInfo[roundindex] += ';e:%d'%(End)
                                    #清标记
                                    Start=None
                                    End  =None
                                    CurLongKey = None
                                    LongPress = False

        #音浪玩法不会跨轮，音乐保证了这一点，所以不考虑这种情况
        roundindex += 1

def LongKeyConvert(key):
    flag = 0x10
    if key=='51':
        return (enmKeyValue_LEFT | flag)
    if key=='52':
        return (enmKeyValue_UP | flag)
    if key=='53':
        return (enmKeyValue_DOWN | flag)
    if key=='54':
        return (enmKeyValue_RIGHT | flag)
    if key=='55':
        return (enmKeyValue_SPACE | flag)
    return 0

def ModifyByLongInfo():
    global gLongInfo,gAllround
    for k in gLongInfo.keys():
        thisround = gAllround[k]
        s = gLongInfo.get(k)
        p = s.split(';')

        key = p[0].split(':')[1]
        key = LongKeyConvert(key)
        
        if len(p) == 3:
            #说明在本轮开始长按，在本轮结束,下一个不为0处即为长按键停止处
            start = int(p[1].split(':')[1])
            end = int(p[2].split(':')[1])
            
            startrule = start/8
            startpos  = start%8
            endrule   = end/8
            endpos    = end%8
            for i in xrange(8):
                if i >= startrule and i < endrule:
                    keySerial = 0
                    for j in xrange(4):
                        keySerial |= (key << 8*j)
                    thisround.rule[i] = int2hex(keySerial)
            #调整开始和结束按键
            value1 = int(thisround.rule[startrule],16)
            value2 = int(thisround.rule[endrule],16)
            for i in xrange(4):
                #开始
                if i >= startpos:
                    value1 |= key << 8*i
                if i < endpos:
                    value2 |= key << 8*i
            
            thisround.rule[startrule] = int2hex(value1)
            thisround.rule[endrule] = int2hex(value2)
                
        else:
            #只可能本轮开始，本轮结束，不会有其它情况出现，若出现，视错误直接忽略
            pass
        

if __name__ == '__main__':
    SaveYL('40003.bms')